﻿using System;
using System.Collections.Generic;
using System.Threading;
using System.Media;

class ReflexAssessment : Game
{
    private readonly long miliseconds = 10000;  // how many miliseconds there are for a game
    private readonly int timeDelay = 50;    // ms to refresh the screen between
    private readonly Random rand = new Random();
    private readonly int windowWidth = Console.WindowWidth;
    private readonly int windowHeight = Console.WindowHeight;
    private List<PressedArrow> pressedArrows = new List<PressedArrow>(20);    // the keys pressed during the game

    public ReflexAssessment(int difficulty)
        : base(difficulty)
    {
        this.difficulty = difficulty;
        this.result = 0;
    }

    public override void StartGame()
    {
        ConsoleTweaks.RemoveScrollBars();
        DrawWelcomeScreen();

        long milisecondsLeft = (miliseconds + miliseconds / difficulty);
        Arrow nextArrow = GenerateArrow();

        while (milisecondsLeft > 0)
        {
            DrawScreen(nextArrow, milisecondsLeft);

            if (Console.KeyAvailable)
            {
                ConsoleKeyInfo keyInfo = Console.ReadKey();

                switch (keyInfo.Key)
                {
                    case ConsoleKey.UpArrow:
                        if (nextArrow == Arrow.UpArrow)
                        {
                            this.result++;
                            pressedArrows.Add(new PressedArrow(nextArrow, true));
                            nextArrow = GenerateArrow();
                        }
                        else
                        {
                            pressedArrows.Add(new PressedArrow(Arrow.UpArrow, false));
                            PunishForWrongKey(ref milisecondsLeft);
                        }
                        break;
                    case ConsoleKey.DownArrow:
                        if (nextArrow == Arrow.DownArrow)
                        {
                            this.result++;
                            pressedArrows.Add(new PressedArrow(nextArrow, true));
                            nextArrow = GenerateArrow();
                        }
                        else
                        {
                            pressedArrows.Add(new PressedArrow(Arrow.DownArrow, false));
                            PunishForWrongKey(ref milisecondsLeft);
                        }
                        break;
                    case ConsoleKey.LeftArrow:
                        if (nextArrow == Arrow.LeftArrow)
                        {
                            this.result++;
                            pressedArrows.Add(new PressedArrow(nextArrow, true));
                            nextArrow = GenerateArrow();
                        }
                        else
                        {
                            pressedArrows.Add(new PressedArrow(Arrow.LeftArrow, false));
                            PunishForWrongKey(ref milisecondsLeft);
                        }
                        break;
                    case ConsoleKey.RightArrow:
                        if (nextArrow == Arrow.RightArrow)
                        {
                            this.result++;
                            pressedArrows.Add(new PressedArrow(nextArrow, true));
                            nextArrow = GenerateArrow();
                        }
                        else
                        {
                            pressedArrows.Add(new PressedArrow(Arrow.RightArrow, false));
                            PunishForWrongKey(ref milisecondsLeft);
                        }
                        break;
                    default:
                        PunishForUnallowedKey();
                        break;
                }
            }

            while (Console.KeyAvailable)
            {
                Console.ReadKey(true);
            }

            Thread.Sleep(timeDelay);
            milisecondsLeft -= timeDelay;
        }

        EndGame();
    }

    private void PunishForUnallowedKey()
    {
        SystemSounds.Beep.Play();
    }

    private void PunishForWrongKey(ref long timeLeft)
    {
        Console.BackgroundColor = ConsoleColor.Red;
        Console.Clear();
        Thread.Sleep((int)(100 * (difficulty / 2f)));
        timeLeft -= (long)(100 * (difficulty / 2f));
    }

    /// <summary>
    /// Draw the player screen
    /// </summary>
    /// <param name="nextArrow"></param>
    /// <param name="milisecondsLeft"></param>
    private void DrawScreen(Arrow nextArrow, long milisecondsLeft)
    {
        Console.BackgroundColor = ConsoleColor.Yellow;
        Console.Clear();

        // printing the time left
        Console.ForegroundColor = ConsoleColor.DarkGreen;
        int timerMaxLength = (miliseconds.ToString() + " S").Length;
        Console.SetCursorPosition(windowWidth - timerMaxLength - 1, 0);
        Console.Write("{0:0.000} S", (double)milisecondsLeft / 1000);
        Console.SetCursorPosition(0, 0);
        Console.Write("Result: {0}", this.result);

        // printing the arrow expected
        Console.ForegroundColor = ConsoleColor.DarkRed;
        ConsoleTweaks.CenterTextOnRow("Press " + nextArrow.ToString(), windowHeight / 2);

        // printing the pressed arrows
        for (int i = pressedArrows.Count - 1, j = windowWidth - 1; (i >= 0) && (j >= 0); i--, j--) // here the list is iterated until the row for printing is filled
        {
            Console.SetCursorPosition(j, windowHeight - 2);
            if (pressedArrows[i].Correct)
            {
                Console.ForegroundColor = ConsoleColor.DarkGreen;
            }
            else
            {
                Console.ForegroundColor = ConsoleColor.DarkRed;
            }
            Console.Write((char)pressedArrows[i].Arrow);
        }
    }

    /// <summary>
    /// Get a random arrow
    /// </summary>
    /// <returns></returns>
    private Arrow GenerateArrow()
    {
        int asciiCode = rand.Next(24, 28);
        return (Arrow)asciiCode;
    }

    public override void EndGame()
    {
        int rightHits = 0, wrongHits = 0;
        foreach (var item in pressedArrows)
        {
            if (item.Correct)
            {
                rightHits++;
            }
            else
            {
                wrongHits++;
            }
        }
        Console.BackgroundColor = ConsoleColor.Yellow;
        Console.Clear();
        ConsoleTweaks.CenterTextOnRow("GAME OVER", windowHeight / 2 - 2);
        ConsoleTweaks.CenterTextOnRow(String.Format("You made {0} points!", this.Result), windowHeight / 2);
        ConsoleTweaks.CenterTextOnRow(String.Format("{0} right hits and {1} erroneous ones.", rightHits, wrongHits), windowHeight / 2 + 2);
        ConsoleTweaks.CenterTextOnRow("Congratulations", windowHeight / 2 + 4);
        Console.SetCursorPosition(windowWidth / 2, windowHeight / 2 + 5);
        Console.ReadKey();
    }

    /// <summary>
    /// Draws the welcome screen of the game
    /// </summary>
    private void DrawWelcomeScreen()
    {
        Console.BackgroundColor = ConsoleColor.Yellow;
        Console.Clear();
        Console.ForegroundColor = ConsoleColor.Red;
        ConsoleTweaks.CenterTextOnRow("Asenoff Entertainment presents", windowHeight / 2 - 2);
        Console.ForegroundColor = ConsoleColor.DarkRed;
        ConsoleTweaks.CenterTextOnRow("REFLEX ASSESSMENT", windowHeight / 2);
        Console.ForegroundColor = ConsoleColor.Red;
        ConsoleTweaks.CenterTextOnRow("Press any key to continue...", windowHeight / 2 + 2);
        Console.SetCursorPosition(windowWidth / 2, windowHeight / 2 + 3);
        Console.ReadKey();
    }
}